package com.honey.spring;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.HashMap;
import java.util.Map;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.java.plugin.Plugin;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;

import com.honey.compilation.xml.XmlElement;
import com.honey.core.Extension;
import com.honey.core.notify.Notify;
import com.honey.core.notify.Signal;
import com.honey.core.notify.SpringSignal;
import com.honey.spring.signal.SignalKind;
import com.honey.spring.signal.SignalKindFactory;

@SuppressWarnings("unchecked")
public class SpringNotifyExtension extends Extension<Plugin> implements Notify {

	private static final Class<Signal>[] SUPPORT_SIGNAL =  new Class[]{	
		SpringSignal.class,
		Signal.class,
	};
	
	private Map<String, Map<String, String>> beanIds  = null;
	
	private File beanFile = null;
	
	public SpringNotifyExtension(Plugin belongPlugin) {
		super(belongPlugin);
		beanIds = new HashMap<String, Map<String, String>>();
		String applicationContext = ((SpringPlugin)belongPlugin).getApplicationContext();

		beanFile = new File( applicationContext );
		if(! applicationContext.endsWith(".xml")){
			if( ! beanFile.exists() ){
				beanFile.mkdirs();
			}
			beanFile = new File( beanFile ,"ApplicationContext.xml");
		}else{
			File parent = beanFile.getParentFile(); 
			if( ! parent.exists() ){
				parent.mkdirs();
			}
		}
		if( ! beanFile.exists() ){
			MakeApplicationContext makeApplicationContext = new MakeApplicationContext();
			makeApplicationContext.makeFile(beanFile);
		}
		try {
			if( beanIds.size() == 0 )
				initApplicationContextFile(beanFile);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/*
	 * (non-Javadoc)
	 * @see com.honey.core.notify.Notify#execute(com.honey.core.notify.Signal)
	 */
	@Override
	public void execute(Signal signal) {
		SignalKind signalKind = SignalKindFactory.getSignalKind(signal);
		if( signalKind != null ){
			File springContextFile = beanFile;
			if( signalKind.context() != null ){
				if( signalKind.context().exists() )
				try {
					initApplicationContextFile( signalKind.context());
				} catch (Exception e) {
					e.printStackTrace();
				}
				springContextFile = signalKind.context();
				
			}
			Map<String, String> map = beanIds.get(springContextFile.getPath()); 
			if( map == null ||! map.containsKey(signalKind.getBeanId())){
				try {
					
					SaveBeanToApplicationContextFile sb = new SaveBeanToApplicationContextFile(springContextFile);
					sb.save(signalKind.getXmlElement());
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.honey.core.notify.Notify#supportSignal()
	 */
	@Override
	public Class<Signal>[] supportSignal() {
		
		return SUPPORT_SIGNAL;
	}
	
	@Override
	public void clean() {
		
	}
	private void initApplicationContextFile(File xmlfile ) throws Exception{
		
		SAXParserFactory sf = SAXParserFactory.newInstance();  
		SAXParser sp = sf.newSAXParser(); 
		class SpringXMLReader extends  DefaultHandler{
			private Map<String, String> beanIds  = null;
			public  SpringXMLReader(Map<String, String> beanIds  ){
				this.beanIds = beanIds;
			}
			public void startElement(String uri, String localName, String qName, Attributes attrs) {
				if("bean".equalsIgnoreCase(qName)){
				    int attrCount = attrs.getLength();
				    for( int i = 0; i < attrCount; i ++ ){
				        String attrName = attrs.getQName(i);
				        if( attrName.equalsIgnoreCase("id") ){
				        	String beanid = attrs.getValue(i);
				        	beanIds.put(beanid, beanid);
				        	break;
				        }
				    }
				}
			}
		};
		String path = xmlfile.getPath();
		Map<String, String> map = beanIds.get(path); 
		if( map == null ){
			map = new HashMap<String, String>();
			beanIds.put(path, map);
		}
		SpringXMLReader reader = new SpringXMLReader(map);
		sp.parse( xmlfile, reader );
	}
	
	class SaveBeanToApplicationContextFile {
		
		private RandomAccessFile randomAccessFile = null;
		
		private long _position = 0 ;
		
		private final long _length ;
		
		public SaveBeanToApplicationContextFile( File target ) throws Exception{
			if( !target.exists() ){
				MakeApplicationContext makeApplicationContext = new MakeApplicationContext();
				makeApplicationContext.makeFile(target);
			}
			this.randomAccessFile = new RandomAccessFile(target,"rw");
			this._length = randomAccessFile.length();
			this._position = _length;
		}
		
		
		/**
		 * 读取文件的最后一行
		 * @param target
		 * @param charset
		 * @return
		 * @throws IOException
		 */
		private String readPreviousLine(String charset) throws IOException {
			String answer = null;
			int legth =0;
			byte b =0;
			while (_position > 0) {
				_position --; 
				randomAccessFile.seek(_position);
				b = randomAccessFile.readByte();
				if ( b == 10){// \n
					break;
				}else if(  b == 13){ // \r
					continue;
				}else{
					legth++;
				}
			}
			if(_position > 0 || legth > 0){
				byte[] bytes = new byte[legth];
				if(_position == 0 )randomAccessFile.seek(0);
				randomAccessFile.read(bytes);
				answer = new String(bytes,0,legth,charset);
			}
			return answer ;
		}
		
		private void save(XmlElement xml ) throws IOException {
			try{
				String line = readPreviousLine("utf-8");
				boolean bool = false ;
				while( line != null){
					line = line.trim();
					if( line.length() > 7 && line.contains("</beans")){
						bool = true ;
						break ;
					}
					line = readPreviousLine("utf-8");
				}
				if( bool ){
					randomAccessFile.seek(_position);
					randomAccessFile.write(
							(new StringBuilder())
							.append("\n")
							.append(xml.compiledContent(1).toString())
							.append("\n</beans>")
							.toString().getBytes("utf-8")
					);
				}
			}finally{
				randomAccessFile.close();
			}
		}
	}
}
